using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using System.Net;

namespace SignalNotifier
{
    public struct Company
    {
        public string code;
        public string name;
    }

    public struct Session
    {
        public float max;
        public float min;
        public float close;
        public int volume;
        public DateTime date;
    }

    struct Process
    {
        public float price;
        public DateTime date;
        public int left;
        public int right;
    }

	/// <summary>
	/// Summary description for ctlMain.
	/// </summary>
	public class ctlMain : System.Windows.Forms.UserControl
    {
        private ListViewColumnSorter lvwColumnSorter;
        private List<string> lstFavoriteCodes;
        private string sListFavoritePath;
        private WebClient webClient;
        private int iMode;
        private string currency;
        private int nMarket;
        private string[] MarketNames;
        private string[] PriceBoardServices;
        private ComboBox cbMarkets;
        private Button btnProcess;
        private ListView lvListSignal;
        private ColumnHeader chCode;
        private ColumnHeader chSellOut;
        private IContainer components;
        private List<float> lstAmplitude;
        private NumericUpDown nudRangeOfPeak;
        private GroupBox groupBox1;
        private NumericUpDown nudLocalRange;
        private NumericUpDown nudRangeToFindThres;
        private ColumnHeader chMaxCurrentMin;
        private NumericUpDown nudNoDayUpperBox;
        private ColumnHeader chGoUpperBox;
        private ColumnHeader chGoUnderBox;
        private WebBrowser wbPriceChart;
        private Label lbCompanyName;
        private List<Company> lstCompanies;
        private NumericUpDown nudBoxHeight;
        private string PriceService;

		public ctlMain()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

			// TODO: Add any initialization after the InitializeComponent call

		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.cbMarkets = new System.Windows.Forms.ComboBox();
            this.btnProcess = new System.Windows.Forms.Button();
            this.lvListSignal = new System.Windows.Forms.ListView();
            this.chCode = new System.Windows.Forms.ColumnHeader();
            this.chSellOut = new System.Windows.Forms.ColumnHeader();
            this.chMaxCurrentMin = new System.Windows.Forms.ColumnHeader();
            this.chGoUpperBox = new System.Windows.Forms.ColumnHeader();
            this.chGoUnderBox = new System.Windows.Forms.ColumnHeader();
            this.nudRangeOfPeak = new System.Windows.Forms.NumericUpDown();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.nudBoxHeight = new System.Windows.Forms.NumericUpDown();
            this.nudNoDayUpperBox = new System.Windows.Forms.NumericUpDown();
            this.nudRangeToFindThres = new System.Windows.Forms.NumericUpDown();
            this.nudLocalRange = new System.Windows.Forms.NumericUpDown();
            this.lbCompanyName = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.nudRangeOfPeak)).BeginInit();
            this.groupBox1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.nudBoxHeight)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudNoDayUpperBox)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudRangeToFindThres)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudLocalRange)).BeginInit();
            this.SuspendLayout();
            // 
            // cbMarkets
            // 
            this.cbMarkets.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cbMarkets.FormattingEnabled = true;
            this.cbMarkets.Location = new System.Drawing.Point(16, 51);
            this.cbMarkets.Name = "cbMarkets";
            this.cbMarkets.Size = new System.Drawing.Size(92, 21);
            this.cbMarkets.TabIndex = 4;
            this.cbMarkets.SelectedIndexChanged += new System.EventHandler(this.cbMarkets_SelectedIndexChanged);
            // 
            // btnProcess
            // 
            this.btnProcess.Location = new System.Drawing.Point(385, 47);
            this.btnProcess.Name = "btnProcess";
            this.btnProcess.Size = new System.Drawing.Size(72, 27);
            this.btnProcess.TabIndex = 5;
            this.btnProcess.Text = "Process";
            this.btnProcess.UseVisualStyleBackColor = true;
            this.btnProcess.Click += new System.EventHandler(this.btnProcess_Click);
            // 
            // lvListSignal
            // 
            this.lvListSignal.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)));
            this.lvListSignal.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.chCode,
            this.chSellOut,
            this.chMaxCurrentMin,
            this.chGoUpperBox,
            this.chGoUnderBox});
            this.lvListSignal.GridLines = true;
            this.lvListSignal.HoverSelection = true;
            this.lvListSignal.Location = new System.Drawing.Point(16, 103);
            this.lvListSignal.Name = "lvListSignal";
            this.lvListSignal.Size = new System.Drawing.Size(501, 544);
            this.lvListSignal.TabIndex = 6;
            this.lvListSignal.UseCompatibleStateImageBehavior = false;
            this.lvListSignal.View = System.Windows.Forms.View.Details;
            this.lvListSignal.MouseClick += new System.Windows.Forms.MouseEventHandler(this.lvListSignal_MouseClick);
            this.lvListSignal.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lvListSignal_ColumnClick);
            // 
            // chCode
            // 
            this.chCode.Text = "Code";
            // 
            // chSellOut
            // 
            this.chSellOut.Text = "Vol Change";
            // 
            // chMaxCurrentMin
            // 
            this.chMaxCurrentMin.Text = "MCm";
            // 
            // chGoUpperBox
            // 
            this.chGoUpperBox.Text = "Go Upper Box";
            // 
            // chGoUnderBox
            // 
            this.chGoUnderBox.Text = "Go Under Box";
            // 
            // nudRangeOfPeak
            // 
            this.nudRangeOfPeak.Location = new System.Drawing.Point(6, 19);
            this.nudRangeOfPeak.Maximum = new decimal(new int[] {
            40,
            0,
            0,
            0});
            this.nudRangeOfPeak.Minimum = new decimal(new int[] {
            10,
            0,
            0,
            0});
            this.nudRangeOfPeak.Name = "nudRangeOfPeak";
            this.nudRangeOfPeak.Size = new System.Drawing.Size(49, 20);
            this.nudRangeOfPeak.TabIndex = 7;
            this.nudRangeOfPeak.Value = new decimal(new int[] {
            20,
            0,
            0,
            0});
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.nudBoxHeight);
            this.groupBox1.Controls.Add(this.nudNoDayUpperBox);
            this.groupBox1.Controls.Add(this.nudRangeToFindThres);
            this.groupBox1.Controls.Add(this.nudLocalRange);
            this.groupBox1.Controls.Add(this.nudRangeOfPeak);
            this.groupBox1.Location = new System.Drawing.Point(114, 16);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(254, 81);
            this.groupBox1.TabIndex = 8;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Parameters";
            // 
            // nudBoxHeight
            // 
            this.nudBoxHeight.Location = new System.Drawing.Point(199, 50);
            this.nudBoxHeight.Maximum = new decimal(new int[] {
            15,
            0,
            0,
            0});
            this.nudBoxHeight.Minimum = new decimal(new int[] {
            10,
            0,
            0,
            0});
            this.nudBoxHeight.Name = "nudBoxHeight";
            this.nudBoxHeight.Size = new System.Drawing.Size(49, 20);
            this.nudBoxHeight.TabIndex = 11;
            this.nudBoxHeight.Value = new decimal(new int[] {
            14,
            0,
            0,
            0});
            // 
            // nudNoDayUpperBox
            // 
            this.nudNoDayUpperBox.Location = new System.Drawing.Point(114, 50);
            this.nudNoDayUpperBox.Maximum = new decimal(new int[] {
            10,
            0,
            0,
            0});
            this.nudNoDayUpperBox.Minimum = new decimal(new int[] {
            1,
            0,
            0,
            0});
            this.nudNoDayUpperBox.Name = "nudNoDayUpperBox";
            this.nudNoDayUpperBox.Size = new System.Drawing.Size(49, 20);
            this.nudNoDayUpperBox.TabIndex = 10;
            this.nudNoDayUpperBox.Value = new decimal(new int[] {
            3,
            0,
            0,
            0});
            // 
            // nudRangeToFindThres
            // 
            this.nudRangeToFindThres.Location = new System.Drawing.Point(6, 50);
            this.nudRangeToFindThres.Maximum = new decimal(new int[] {
            200,
            0,
            0,
            0});
            this.nudRangeToFindThres.Minimum = new decimal(new int[] {
            120,
            0,
            0,
            0});
            this.nudRangeToFindThres.Name = "nudRangeToFindThres";
            this.nudRangeToFindThres.Size = new System.Drawing.Size(49, 20);
            this.nudRangeToFindThres.TabIndex = 9;
            this.nudRangeToFindThres.Value = new decimal(new int[] {
            120,
            0,
            0,
            0});
            // 
            // nudLocalRange
            // 
            this.nudLocalRange.Location = new System.Drawing.Point(114, 19);
            this.nudLocalRange.Maximum = new decimal(new int[] {
            40,
            0,
            0,
            0});
            this.nudLocalRange.Minimum = new decimal(new int[] {
            10,
            0,
            0,
            0});
            this.nudLocalRange.Name = "nudLocalRange";
            this.nudLocalRange.Size = new System.Drawing.Size(49, 20);
            this.nudLocalRange.TabIndex = 8;
            this.nudLocalRange.Value = new decimal(new int[] {
            20,
            0,
            0,
            0});
            // 
            // lbCompanyName
            // 
            this.lbCompanyName.AutoSize = true;
            this.lbCompanyName.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(163)));
            this.lbCompanyName.Location = new System.Drawing.Point(532, 84);
            this.lbCompanyName.Name = "lbCompanyName";
            this.lbCompanyName.Size = new System.Drawing.Size(0, 16);
            this.lbCompanyName.TabIndex = 9;
            // 
            // ctlMain
            // 
            this.BackColor = System.Drawing.Color.White;
            this.Controls.Add(this.lbCompanyName);
            this.Controls.Add(this.btnProcess);
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.lvListSignal);
            this.Controls.Add(this.cbMarkets);
            this.Name = "ctlMain";
            this.Size = new System.Drawing.Size(1049, 650);
            this.Load += new System.EventHandler(this.ctlMain_Load);
            ((System.ComponentModel.ISupportInitialize)(this.nudRangeOfPeak)).EndInit();
            this.groupBox1.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.nudBoxHeight)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudNoDayUpperBox)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudRangeToFindThres)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.nudLocalRange)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

		}
		#endregion

		private void ctlMain_Load(object sender, System.EventArgs e)
		{
            this.lstCompanies = new List<Company>();
            this.lstAmplitude = new List<float>();
            lvwColumnSorter = new ListViewColumnSorter();
            this.lvListSignal.ListViewItemSorter = lvwColumnSorter;
            // 
            // wbPriceChart
            // 
            this.wbPriceChart = new System.Windows.Forms.WebBrowser();
            this.wbPriceChart.Location = new System.Drawing.Point(528, 50);
            this.wbPriceChart.MinimumSize = new System.Drawing.Size(20, 20);
            this.wbPriceChart.Name = "wbPriceChart";
            this.wbPriceChart.Size = new System.Drawing.Size(507, 544);
            this.wbPriceChart.TabIndex = 9;
            this.Controls.Add(this.wbPriceChart);
            // Initialize config
            StreamReader sr = new StreamReader("config.txt");
            while (!sr.EndOfStream)
            {
                switch (sr.ReadLine().Normalize())
                {
                    case "[currency]":
                        currency = sr.ReadLine().Trim();
                        break;
                    case "[nMarkets]":
                        nMarket = int.Parse(sr.ReadLine());
                        PriceBoardServices  = new string[nMarket];
                        MarketNames = new string[nMarket];
                        for (int i = 0; i < nMarket; i++)
                        {
                            MarketNames[i] = sr.ReadLine().Trim();
                            PriceBoardServices[i] = sr.ReadLine().Trim();
                            cbMarkets.Items.Add(MarketNames[i]);
                        }
                        cbMarkets.SelectedIndex = 0;
                        break;
                    case "[favorite]":
                        sListFavoritePath = sr.ReadLine().Trim();
                        break;
                    case "[Amplitude]":
                        if (int.Parse(sr.ReadLine()) != nMarket)
                        {
                            MessageBox.Show("[Amplitude]", "Loi doc file config");
                            return;
                        }
                        for (int i = 0; i < nMarket; i++)
                            lstAmplitude.Add(float.Parse(sr.ReadLine().Trim()));
                        break;
                    case "[Tech chart]":
                        PriceService = sr.ReadLine().Trim();
                        break;
                }
            }
            sr.Close();
		}

        private string convertCurrency(string c)
        {
            if (currency == "VN")
                return c.Replace(",","").Replace('.', ',');
            return c.Replace(",", "");
        }

        private void btnProcess_Click(object sender, EventArgs e)
        {
            // Lay danh sach ma chung khoan
            // Voi moi ma chung khoan
            //      Xac dinh cac chi bao
            lvListSignal.Items.Clear();
            string code, name;
            float thres = (lstAmplitude[cbMarkets.SelectedIndex] - 0.5f) / 100.0f;
            float thresAmp = (lstAmplitude[cbMarkets.SelectedIndex] / 100.0f);
            float boxHeight = (float)nudBoxHeight.Value / 100.0f;
            List<Process> lstSupportThres = new List<Process>();
            List<Process> lstResistThres = new List<Process>();
            lvListSignal.Items.Clear();
            lvwColumnSorter.Order = SortOrder.None;
            lvwColumnSorter.SortColumn = 0;

            for (int i = 0; i < lstCompanies.Count; i++)
            {
                // load du lieu cua mot ma chung khoan
                code = lstCompanies[i].code;
                name = lstCompanies[i].name;
                List<Session> lstSession = GetData(cbMarkets.Text, code, (int)nudRangeToFindThres.Value);
                // Xu ly
                // Tim cac nguong khang cu, ho tro ga^`n
                // Kiem tra peak volume
                // Tim min - curent - max
                // Go upper box
                // Go under box
                //FindSupportThre(lstSession, ref lstSupportThres, ref lstResistThres);
                lvListSignal.Items.Add(code);
                // Kiem tra peak volume
                lvListSignal.Items[i].SubItems.Add(VolumeChange(lstSession).ToString());
                // Ti le (Max - current)/(Current - min)
                lvListSignal.Items[i].SubItems.Add(MaxCurrentMin(lstSession).ToString());
                // Go upper Box
                lvListSignal.Items[i].SubItems.Add(GoUpperBox(lstSession, (int)nudLocalRange.Value, boxHeight));
                // Go under box
                lvListSignal.Items[i].SubItems.Add(GoUnderBox(lstSession, (int)nudLocalRange.Value, boxHeight));
                // Hien thi
                // Xoa data trong bo nho
                lstSession.Clear();
            }
        }

        private List<Session> GetData(string market, string code, int range)
        {
            List<Session> lstSession = new List<Session>();
            if (!File.Exists(market + "\\Updated Data\\" + code + ".txt"))
            {
                MessageBox.Show("File not found!", code);
                return lstSession;
            }
            StreamReader sr = new StreamReader(market + "\\Updated Data\\" + code + ".txt");

            int count = 0;
            while (!sr.EndOfStream)
            {
                string s = sr.ReadLine().Trim();
                // Neu may tieng Viet
                if (currency.ToUpper() == "VN")
                    s = s.Replace('.', ',');

                if (s != "")
                {
                    string[] strs = s.Split(' ', '\t');
                    Session session = new Session();
                    try
                    {
                        session.date = DateTime.ParseExact(strs[0], "dd/MM/yyyy", null);
                    }
                    catch (Exception exc)
                    {
                        MessageBox.Show(exc.Message, "Error at GetData " + market + "\\Updated Data\\" + code + ".txt");
                        return null;
                    }
                    session.max = float.Parse(strs[1]);
                    session.min = float.Parse(strs[2]);
                    session.close = float.Parse(strs[3]);
                    session.volume = int.Parse(strs[4]);
                    lstSession.Add(session);
                    count++;
                    if (count == range)
                        break;
                }
            }
            sr.Close();
            return lstSession;
        }

        private void FindSupportThre(List<Session> sessions, ref List<Process> LowProcessed, ref List<Process> HighProcessed)
        {
            Process LowProcessing;
            Process HighProcessing;
            LowProcessing.price = 10000.0f;
            LowProcessing.date = DateTime.Now;
            LowProcessing.left = LowProcessing.right = 0;
            HighProcessing.price = -1.0f;
            HighProcessing.date = DateTime.Now;
            HighProcessing.left = HighProcessing.right = 0;

            int count = sessions.Count;
            LowProcessed.Clear();
            HighProcessed.Clear();

            for (int i = 0; i < count; i++)
            {
                if (sessions[i].close >= LowProcessing.price)
                {
                    LowProcessing.right++;
                }
                else
                {
                    LowProcessing.price = sessions[i].close;
                    LowProcessing.left += (LowProcessing.right+ 1);
                    LowProcessing.right = 0;
                    LowProcessing.date = sessions[i].date;
                }
                if (sessions[i].close > 45)
                {
                    int l = 0;
                }
                if (sessions[i].close <= HighProcessing.price)
                {
                    HighProcessing.right++;
                }
                else
                {
                    HighProcessing.price = sessions[i].close;
                    HighProcessing.left += (HighProcessing.right + 1);
                    HighProcessing.right = 0;
                    HighProcessing.date = sessions[i].date;
                }

                if (LowProcessing.left >= nudLocalRange.Value && LowProcessing.right > nudLocalRange.Value)
                {
                    LowProcessed.Add(LowProcessing);
                    LowProcessing.price = 10000.0f;
                    LowProcessing.left = 0;
                    LowProcessing.right = 0;
                }
                if (HighProcessing.left >= nudLocalRange.Value && HighProcessing.right > nudLocalRange.Value)
                {
                    HighProcessed.Add(HighProcessing);
                    HighProcessing.price = -1.0f;
                    HighProcessing.left = 0;
                    HighProcessing.right = 0;
                }
            }
        }

        private float MaxCurrentMin(List<Session> sessions)
        {
            float max, min, current;
            max = -1.0f;
            min = 10000.0f;
            current = sessions[0].close;
            int range = Math.Min((int)nudLocalRange.Value, sessions.Count);
            
            for (int i = 0; i < range; i++)
            {
                float close = sessions[i].close;
                if (close > max)
                    max = close;
                if (close < min)
                    min = close;
            }
            if (current == min)
                return 1000000.0f;
            return (max - current) / (current - min);
        }

        private float VolumeChange(List<Session> sessions)
        {
            int current = sessions[0].volume;
            int range = Math.Min((int)nudLocalRange.Value, sessions.Count);
            int accVol = 0;
            for (int i = 1; i < range; i++)
            {
                accVol += sessions[i].volume;
            }
            return ((float)current) / accVol * (range - 1);
        }

        private string GoUpperBox(List<Session> sessions, int range, float boxHeight)
        {
            float max, min;
            max = -1.0f;
            min = 100000.0f;
            int NoDay = (int)nudNoDayUpperBox.Value;
            range = Math.Min(range, sessions.Count);

            for (int i = range-1; i >= 1; i--)
            {
                float close_price = sessions[i].close;
                if (close_price > max)
                    max = close_price;
                if (close_price < min)
                    min = close_price;

                if ((max - min) / (max + min) * 2 < boxHeight && i < NoDay)
                    if (sessions[i-1].close > max)
                        return "1";
            }
            return "0";
        }

        private string GoUnderBox(List<Session> sessions, int range, float boxHeight)
        {
            float max, min;
            max = -1.0f;
            min = 100000.0f;
            int NoDay = (int)nudNoDayUpperBox.Value;
            range = Math.Min(range, sessions.Count);

            for (int i = range-1; i >= 1; i--)
            {
                float close_price = sessions[i].close;
                if (close_price > max)
                    max = close_price;
                if (close_price < min)
                    min = close_price;

                if ((max - min) / min < boxHeight && i < NoDay)
                    if (sessions[i - 1].close < min)
                        return "1";
            }
            return "0";
        }

        private void lvListSignal_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == lvwColumnSorter.SortColumn)
            {
                // Reverse the current sort direction for this column.
                if (lvwColumnSorter.Order == SortOrder.Ascending)
                {
                    lvwColumnSorter.Order = SortOrder.Descending;
                }
                else
                {
                    lvwColumnSorter.Order = SortOrder.Ascending;
                }
            }
            else
            {
                // Set the column number that is to be sorted; default to ascending.
                lvwColumnSorter.SortColumn = e.Column;
                lvwColumnSorter.Order = SortOrder.Ascending;
            }

            // Perform the sort with these new sort options.
            this.lvListSignal.Sort();
        }

        private int BinSearch(List<Company> lstCompanies, string code)
        {
            /*int l = 0, r = lstCompanies.Count - 1;
            
            while (l <= r)
            {
                int mid = (l + r) / 2;
                int rsl = lstCompanies[mid].code.CompareTo(code);
                if (rsl == 0)
                    return mid;
                if (rsl < 0)
                    l = mid + 1;
                if (rsl > 0)
                    r = mid - 1;
            }
            return -1;*/
            for (int i = 0; i < lstCompanies.Count; i++)
                if (lstCompanies[i].code == code)
                    return i;
            return -1;
        }

        private void lvListSignal_MouseClick(object sender, MouseEventArgs e)
        {
            if (lvListSignal.SelectedItems.Count > 0)
            {
                string code = lvListSignal.SelectedItems[0].Text;
                int idx = BinSearch(lstCompanies, code);
                if (idx >= 0)
                    lbCompanyName.Text = code + " - " + lstCompanies[idx].name;
                else
                    lbCompanyName.Text = code;
                string path;
                // Price
                path = PriceService.Replace("<code>", code).Replace("<style>", "candle").Replace("<range>", "3m");
                wbPriceChart.Navigate(path);
            }
        }

        private void cbMarkets_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Load danh sach cong ty
            lstCompanies.Clear();
            StreamReader sr = new StreamReader(cbMarkets.Text + "\\listCompanies.txt");
            while (!sr.EndOfStream)
            {
                Company comp = new Company();
                comp.code = sr.ReadLine().Trim();
                comp.name = sr.ReadLine().Trim();
                lstCompanies.Add(comp);
            }
            sr.Close();
        }
    }
}
